home *** CD-ROM | disk | FTP | other *** search
/ HPAVC / HPAVC CD-ROM.iso / PXDTUT4.ZIP / PXDTUT4.TXT < prev   
Text File  |  1997-06-21  |  24KB  |  634 lines

  1.  
  2.  
  3.                   |====================================|
  4.                   |                                    |
  5.                   |   TELEMACHOS proudly presents :    |
  6.                   |                                    |
  7.                   |    Part 4 of the PXD trainers  -   |
  8.                   |                                    |
  9.                   |          3D Vector engine          |
  10.                   |         Differnt poly-fills        |
  11.                   |                                    |
  12.                   |====================================|
  13.  
  14.          ___---__-->   The Peroxide Programming Tips   <--__---___
  15.  
  16. <><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>
  17.  
  18.  
  19. Intoduction
  20. -----------
  21.  
  22. As promised in my last tuturial this one will be on different types of fills
  23. that we can add to our basic 3D engine.
  24. If you have not read my previous tuturial (pxdtut3.zip) I suggest you get a
  25. copy of it as we'll use some code we discussed in it.
  26. This tuturial is about one (two) week late. This is because some work came up -
  27. involving a trip to a danish island with about 60 kids in the age 4-6 years,
  28. arranging a local sailing tournement and eating lots of sweets while drinking
  29. softdrinks and watching video.
  30. So as you see I'm a busy man 8)
  31.  
  32. But well.. better late than never - here goes :
  33.  
  34.     - Z-shading : Bad type of flatshading... but I'll tell you anyway...
  35.     - Flat shading according to moving lightsources
  36.     - Gouraud shading according to moving lightsources
  37.     - Texturemapping
  38.     - Environmentmapping / Fake Phong....
  39.  
  40.  
  41.  
  42. Z-SHADING
  43. -----------
  44.  
  45. This one you can implement in about 2 minutes in the 3D object engine we build
  46. in the last tuturial.
  47. The theory behind Z-shading is that a face usually darkens when moving farther
  48. away from the light.
  49. As we store the center Z-values for all our faces in a variable called Centers
  50. we can easily determine the distance from the light to the center of the face.
  51. Now we convert this Z-value into a value that lies in the colorspan we use for
  52. shades.
  53. As you have probably noticed this means that the lightsource is placed at a
  54. fixed position somewhere outside the screen pointing straight into the screen.
  55.  
  56. Take a look at this piece of code to see how it could be implemented :
  57.  
  58.  
  59. Procedure BadFlatShade(where : word; minZ, maxZ, Num_of_shades : integer);
  60. {********************************************************************}
  61. {**  MinZ, MaxZ : What is the minimum and maximum Z-values of the  **}
  62. {**               faces that is to be drawn ? You COULD set theese **}
  63. {**               values so that minZ is the minimum Z-val of the  **}
  64. {**               entire object and MaxZ the maximum value. However**}
  65. {**               consider the fact that half of the objects faces **}
  66. {**               is removed by hidden face removal. So, if you    **}
  67. {**               want to have bigger diference on the shown faces **}
  68. {**               just set minZ to minimum object Z-value and MaxZ **}
  69. {**               to the Z-value of the CENTER of the object.      **}
  70. {**               Experiment!!                                     **}
  71. {** Num_of_shades : shades used = color 0 to Num_of_shades         **}
  72. {********************************************************************}
  73.  
  74. var
  75.  taeller : integer;
  76.  X1,Y1,X2,Y2,X3,Y3,X4,Y4 : integer;
  77.  color : byte;
  78.  polynr : integer;
  79.  normal,span : integer;
  80.  shade : real;
  81. begin
  82.  for taeller := 1 to Num_of_faces do
  83.    begin
  84.      polynr := OrderTable[taeller];
  85.      X1 := translated[faces[polynr].P1].X;
  86.      Y1 := translated[faces[polynr].P1].Y;
  87.      X2 := translated[faces[polynr].P2].X;
  88.      Y2 := translated[faces[polynr].P2].Y;
  89.      X3 := translated[faces[polynr].P3].X;
  90.      Y3 := translated[faces[polynr].P3].Y;
  91.      X4 := translated[faces[polynr].P4].X;
  92.      Y4 := translated[faces[polynr].P4].Y;
  93.  
  94.      {***************** Z-shading *****************}
  95.  
  96.      span := ABS (minZ-maxZ);   {Z span of object}
  97.      shade := (centers[taeller] div 4 + ABS(minZ)) / span;
  98.  
  99.      color := Num_of_shades - round(Num_of_shades*shade);
  100.  
  101.      {*******************************************************}
  102.      {******* HIDDEN FACE REMOVAL - YES, THAT EASY ;) *******}
  103.      {*******************************************************}
  104.      {Z-Comp of normal to 2d-polygon}
  105.      normal := (Y1-Y3)*(X2-X1) - (X1-X3)*(Y2-Y1);
  106.        if (normal < 0) then {pointing towards us}
  107.          Polygon(X1,Y1,X2,Y2,X3,Y3,X4,Y4,color,where);
  108.      {*******************************************************}
  109.      {*******************************************************}
  110.      {*******************************************************}
  111.    end;
  112. end;
  113.  
  114.  
  115.  
  116.  
  117. NICE FLATSHADING - THE THEORY BEHIND THE LIGHTVECTOR / DOT-PRODUCT
  118. -------------------------------------------------------------------
  119.  
  120. Now... while the above shown piece of code works quite allright on simple
  121. objects as fx. cubes it will not produce an acceptable shading for more complex
  122. objects.
  123. And the situation with the fixed lightsource is'nt to good either. So we'll
  124. have to find some better way of shading our objects. Remember the facenormals
  125. we discussed in the last tuturial - I told you they could be used for shading
  126. and I did not lie :)
  127.  
  128. To refresh your memory I'll just show you the formulas again :
  129.  
  130.         Xnormal=(P2.Y-P1.Y)(P1.Z-P3.Z)-(P2.Z-P1.Z)(P1.Y-P3.Y)
  131.         Ynormal=(P2.Z-P1.Z)(P1.X-P3.X)-(P2.X-P1.X)(P1.Z-P3.Z)
  132.         Znormal=(P2.X-P1.X)(P1.Y-P3.Y)-(P2.Y-P1.Y)(P1.X-P3.X)
  133.  
  134. Now, if we define the lightsource as a vector also :
  135.  
  136.         LightVect  : RealPointT;
  137.  
  138. then the direction of the lightsource can be set by defining LightVect.X,
  139. LightVect.Y and LightVect.Z
  140.  
  141. Now, the amount of light that shines on an object is determined by the angle
  142. between the lightsource and the facenormal. Take a look at this picture..
  143.  
  144.                 normal
  145.                   |         / <-- the lightsource
  146.                   |       /
  147.                   |_ A  /
  148.                   |  \/
  149.                   | /
  150.                   |
  151.         ---------------------  <-- a face in the object
  152.  
  153.  
  154. If the light shines directly on the face a maximum amount of light should be
  155. shown, and the angle A would be 0 degree.
  156. The greater the angle, the darker shade.
  157.  
  158. Now is the time for another new term - UNITVECTOR. A unitvector is simply a
  159. vector with the length 1. Any vector can easily be made a unitvector by
  160. dividing all three vectorcomponents by the entire vector length.
  161. The length of a vector is calculated by :
  162.  
  163.     length = SQRT(X*X + Y*Y + Z*Z);
  164.  
  165. Obviously it's WAY to slow to :
  166.  
  167. 1) Calculate the facenormal.
  168. 2) Calculate the length of the normal (SQRT is SLOOOW)
  169. 3) Divide all three components by length
  170.  
  171. So what we'll do is to calculate the facenormals and make them unitvectors ONE
  172. time when setting up the object. Then we rotate these unitvectors for each
  173. frame. This is of cause not entirely true. If we made our facenormals unit-
  174. vectors during setup we would have to store all vectorcomponents as reals.
  175. And then our rotation routine could not handle them. So we store the unit-
  176. vectors as fx. 8.8 fixed point values. As all vectorcomponents range from 0
  177. to 1 we could easily store them as 1.15 fixed point values... but if we store
  178. them as 8.8 it'll make environmentmapping easier... :)
  179. As with the object itself, store the original normalvectors in a buffer and
  180. rotate the buffer into another buffer from which you do all the calculations
  181. on the rotated normals - this way you'll not lose precision during rotation.
  182.  
  183. Now, if we define both the facenormal and the lightvector as UNITVECTORS
  184. we can use a new formula called the dot-product to calculate the angle between
  185. them. Actually the dot-product returns the cosinus values of the angle - but
  186. this is PERFECT!
  187. As cos ranges from 0 to 1 and being 1 at 0 degrees and 0 at 90 degrees we can
  188. just multiply the cosinus value by the numbers of shades we want in our object.
  189.  
  190. The dot-product :
  191.  
  192. dot := (Normal.X*Lightvect.X) + (Normal.Y*Lightvect.Y) + (Normal.Z*Lightvect.Z);
  193.  
  194.  
  195. Now go implement this in your engine.... It COULD look like this :
  196.  
  197.  
  198. Procedure NiceFlatShade(where : word; Num_of_shades : integer);
  199. var
  200.  taeller : integer;
  201.  X1,Y1,X2,Y2,X3,Y3,X4,Y4 : integer;
  202.  color : byte;
  203.  polynr : integer;
  204.  normal : integer;
  205.  shade : real;
  206.  Nx,Ny,Nz : real;
  207.  dot : real;
  208.  
  209. begin
  210.  for taeller := 1 to Num_of_faces do
  211.    begin
  212.      polynr := order[taeller];
  213.      X1 := translated[faces[polynr].P1].X;
  214.      Y1 := translated[faces[polynr].P1].Y;
  215.      X2 := translated[faces[polynr].P2].X;
  216.      Y2 := translated[faces[polynr].P2].Y;
  217.      X3 := translated[faces[polynr].P3].X;
  218.      Y3 := translated[faces[polynr].P3].Y;
  219.      X4 := translated[faces[polynr].P4].X;
  220.      Y4 := translated[faces[polynr].P4].Y;
  221.  
  222.  
  223.      {*******************************************************}
  224.      {******* HIDDEN FACE REMOVAL - YES, THAT EASY ;) *******}
  225.      {*******************************************************}
  226.      {Z-Comp of normal to 2d-polygon}
  227.      normal := (Y1-Y3)*(X2-X1) - (X1-X3)*(Y2-Y1);
  228.        if (normal < 0) then {pointing towards us}
  229.          begin
  230.            {************************************************************}
  231.            {**   LAMBERTS FLATSHADING ACCORDING TO MOVING LIGHTSOURCE **}
  232.            {************************************************************}
  233.  
  234.           Nx := RotNormals[polynr].X / 256;
  235.           Ny := RotNormals[polynr].Y / 256;
  236.           Nz := RotNormals[polynr].Z / 256;
  237.           dot := (Nx*Lightvect.X) + (Ny*Lightvect.Y) + (Nz*Lightvect.Z);
  238.           if (dot > 1) or (dot < 0) then dot := 0;
  239.           color := Round(dot * Num_of_shades);
  240.           Polygon(X1,Y1,X2,Y2,X3,Y3,X4,Y4,color,where);
  241.          end;
  242.      {*******************************************************}
  243.      {*******************************************************}
  244.      {*******************************************************}
  245.    end;
  246. end;
  247.  
  248.  
  249.  
  250. GOURAUD SHADING
  251. ----------------
  252.  
  253. After Flatshading comes Gouraud shading. Gouraud shading is the first shading
  254. type which does not have a constant color for a face in the object.
  255. First thing to say is, that gouraud shading is based on linear interpolation
  256. of colorvalues/lightintensities.
  257. We still draw the polygon scanline pr scanline, but now we need more than just
  258. two X-values to draw the line. We also needs two colors per line. Namely the
  259. starting color and the ending color. When we draw our horizontal line, we use
  260. fixed point math to step through the colorspan the line consist of.
  261. Take a look at this gouraud line drawer :
  262.  
  263.  
  264. PROCEDURE GouraudHorline(xbeg,xend,y:integer; c1,c2:byte;where : word);
  265. var coloradd : integer;
  266. begin
  267.  if (Xend-Xbeg) <> 0 then
  268.  coloradd := ((c2-c1) shl 8) div (Xend-Xbeg);
  269.  
  270.  asm
  271.   mov bx,[xbeg]
  272.   mov cx,[Xend]
  273.  
  274.   inc cx
  275.   sub cx,bx             { length of line in cx }
  276.   mov es,Where          { segment to draw in   }
  277.   mov ax,[y]            { Ypos of the line     }
  278.   shl ax,6
  279.   mov di,ax
  280.   shl ax,2
  281.   add di,ax             { y*320 in di (offset) }
  282.   add di,bx             { add x-begin }
  283.  
  284.   xor ax,ax
  285.   mov al,[C1]
  286.   shl ax,8              {colorstart fixed-p 8.8 }
  287.  
  288.  @again:
  289.   mov es:[di],ah        {ah = real value of fixed-p color (ah = ax shr 8 ) }
  290.   inc di
  291.   dec cx
  292.   add ax,[coloradd]
  293.   cmp cx,0
  294.   jne @again
  295.  end;
  296. end;
  297.  
  298.  
  299. Thats all well and good.....  but how do we calculate C1 and C2 for each
  300. horizontal line ?
  301. Well.. for a start we'll calculate the color for each of the 4 points in our
  302. polygon. This is done in a way similar to the way we calculated the face color
  303. in flat shading - by calculating the dot-product of the lightvector and the
  304. POINT-normal.
  305. Now, as you all know, one can't calculate a normal to a point. It has to be a
  306. plane. So we'll have to think of our own way of defining the term POINT-normal.
  307. It has been decided that the normal to a point is calculated by taking the
  308. average of the FACE-normals in which the point is included.
  309. You could calculate the 4 normals pr. face each frame - or you could calculate
  310. them once during setup and then rotate them like the face normals.
  311. Suit yourself.
  312. Remember!  The POINT-normals has to be unitvectors. The fact that the
  313. FACE-normals are unitvectors does NOT mean that the calculated POINT-normals
  314. will be too.
  315. So, you'll have to make them unitvectors for each frame.
  316. All this means that the solution with the POINT-normals being rotated is
  317. probably the best/fastest :)
  318.  
  319. When you got the 4 color values for the 4 points of the polygon you scan the
  320. edges as with the normal polygon routine I showed you in the last tut.
  321. But as you scan along the edges you shade them at the same time - much like
  322. the GouraudHorLine procedure.... the difference is just that the line you
  323. shade is'nt horizontal. In the end, you'll have two variables filled with the
  324. info needed to draw our gouraud shaded polygon :
  325.  
  326.     polygon[1..200,1..2] :  the X-values to draw the lines between
  327.     color[1..200,1..2]   :  the starting and ending colors for each HorLine
  328.  
  329. I'll just show you the new ScanPolySide procedure :
  330.  
  331.  
  332. Procedure ScanPolySide(x1,y1,x2,y2:integer;c1,c2 : byte);
  333.   { This scans the side of a polygon and updates the poly variable }
  334.   {updates the colors variable for gouraud shading}
  335. VAR temp:integer;
  336.     xfixed,xinc,x:integer;
  337.     loop1:integer;
  338.     dcol : integer;
  339.     color : integer;
  340. BEGIN
  341.   if y1=y2 then exit;
  342.   if y2<y1 then
  343.    BEGIN
  344.      temp:=y2;
  345.      y2:=y1;
  346.      y1:=temp;
  347.      temp:=x2;
  348.      x2:=x1;
  349.      x1:=temp;
  350.      temp := c2;
  351.      c2 := c1;
  352.      c1 := temp;
  353.    END;          {make sure y1 is top and y2 bottom}
  354.  
  355.   dcol := ((c2-c1) shl 8) div (Y2-Y1);    {colorstep pr. y-line}
  356.   color := c1 shl 8;                      {starting color in fixed-p}
  357.  
  358.   xinc:=((x2-x1) shl 7) div (y2-y1);      {xinc in fixed point}
  359.   xfixed:=x1 shl 7;
  360.   for loop1:=y1 to y2 do BEGIN
  361.     if (loop1>(ytopclip)) and (loop1<(ybotclip)) then
  362.       BEGIN
  363.         x := xfixed shr 7;
  364.         if (x<polygon[loop1,1]) then
  365.          begin
  366.            polygon[loop1,1]:=x;
  367.            colors[loop1,1] := color shr 8;
  368.          end;
  369.         if (x>polygon[loop1,2]) then
  370.          begin
  371.            polygon[loop1,2]:=x;
  372.            colors[loop1,2] := color shr 8;
  373.          end;
  374.       END;
  375.     xfixed:=xfixed+xinc;
  376.     color := color + dcol;
  377.   END;
  378. END;
  379.  
  380.  
  381.  
  382. Now... that was'nt too hard ehh ??
  383. Put this new ScanPolySide into your polygon routine and change the call to
  384. HorLine to GouraudHorLine with the parameters :
  385.  
  386.       GouraudHorline(polygon[loop,1],polygon[loop,2],loop,
  387.                      colors[loop,1], colors[loop,2]);
  388.  
  389. That should do the trick - a nice Gouraud shaded polygon.
  390. Check out the sample program if you have any trouble coding this effect.
  391.  
  392.  
  393.  
  394. TEXTUREMAPPING
  395. ---------------
  396.  
  397. Now, the first thing I would like to say in this section is that the texture
  398. mapping we'll do here differs ALOT from the one I wrote about in tuturial 1.
  399. The difference is that while the texturemapping in tuturial 1 had correct
  400. perspective this type won't.
  401. The reason we could do perspectively correct texturemapping in tut 1 was that
  402. all the polygons we mapped had constant Z-values for each VERTICAL scanline.
  403. That means that all the perspective calculations only needs to be calculated
  404. ONCE pr scanline. We could do the same thing with  polygons with constant
  405. Z-values for each HORIZONTAL scanline.
  406. But the polygon we're mapping today is often rotated so there is NO constant
  407. Z-values. Therefor heavy calculation is needed for each pixel in the polygon
  408. to calculate the u,v coordinate in the texture.
  409.  
  410. So, what we'll do is called a linear texturemapping. It works fine on the kind
  411. of objects that is seen in demos 'cause they often move to fast for the viewer
  412. to see the perspective errors.
  413.  
  414. We'll use the same polygon drawing routine as for all the other fills. The only
  415. diffence lies in the ScanPolySide and in the horizontal line drawer -
  416. U probably allready guessed that :)
  417.  
  418. When calling the TextureMappedPolygon routine we assign 4 texture coordinates
  419. - one to each point in the polygon. We call these coordinates :
  420.  
  421.   U1,V2, U2, ... , V4
  422.  
  423. When scanning the four sides in the polygon we also store two texture
  424. coordinates for each horizontal line. The implemention of this is VERY much
  425. like the one in gouraud shading - only with one more value to increment.
  426. Check out the sample program if you have any trouble.
  427.  
  428. Now for the TextureMappedHorline routine ;)
  429. It has to scan through the texturemap while drawing the horizontal line. It is
  430. quite easy to do, using fixed point math :
  431.  
  432.  
  433. PROCEDURE TextureMapHorline(xbeg,xend,y,u1,v1,u2,v2:integer;source,dest : word);
  434. var
  435.   DeltaX : integer;
  436.   DeltaY : integer;
  437.  
  438. begin
  439.   If (Xend-Xbeg) <> 0 then
  440.    begin
  441.      DeltaX := ((u2-u1) shl 7) div (Xend-Xbeg);
  442.      DeltaY := ((v2-v1) shl 7) div (Xend-Xbeg);  { 9.7 fixed-p}
  443.      DeltaX := DeltaX + DeltaX;
  444.      DeltaY := DeltaY + DeltaY;                  {now 8.8 fixed-p :)  }
  445.    end
  446.     else
  447.    begin
  448.     DeltaX := 0;
  449.     DeltaY := 0;
  450.    end;
  451. asm
  452.   push ds
  453.   mov ax, [source]
  454.   mov ds,ax
  455.  
  456.   mov bx,[xbeg]
  457.   mov cx,[Xend]
  458.   inc cx
  459.   sub cx,bx            {cx =  length of line}
  460.  
  461.   mov es,dest
  462.   mov ax,[y]
  463.   shl ax,6
  464.   mov di,ax
  465.   shl ax,2
  466.   add di,ax
  467.   add di,bx           {es:[di] start of line}
  468.  
  469.   mov ah,byte[v1]   {8.8 fixed-p value of YTexturePos - for easy ofs calc}
  470.   mov al,byte[u1]
  471.   mov si,ax         {si = starting offset in texture }
  472.   mov dh,al         {8.8 fixed-p value of XTexturePos - for easy ofs calc}
  473.  
  474. @again:
  475.   movsb               {draw byte}
  476.   add ax,[DeltaY]     {advance in texturemap}
  477.   add dx,[DeltaX]     {advance in texturemap}
  478.  
  479.   mov bh,ah           {bh = Ypos * 256 }
  480.   mov bl,dh           {bl = Xpos_fixed / 256  = Xpos_real}
  481.   mov si,bx           {BX = Ypos_real * 256 + Xpos_real = offset}
  482.  
  483.   dec cx
  484.   cmp cx,0
  485.   jne @again          {are we finished ??  }
  486.  
  487.   pop ds
  488. end;
  489. end;
  490.  
  491.  
  492. As you see our Texture has to be placed in a 256X256 orientated coordinate
  493. system. This does not mean that the texture HAS to be 256X256, but it must be
  494. stored with 256 bytes pr line.
  495. This means you probably has to rewrite your image loader a little - and if you
  496. normally use a virtuel screen with the size 320X200 you have to allocate a
  497. little more memory. 320X200 = 64000 bytes while 256X256 = 64Kb... NOT the
  498. same :)
  499.  
  500.  
  501.  
  502. ENVIRONMENT MAPPING / PHONG SHADING
  503. ------------------------------------
  504.  
  505. What is environement mapping ? Well.. environment mapping is an effect where
  506. an image is reflected on a shiny surface.
  507. The implementation is a straight texturemap - the only thing there is to
  508. environment mapping is calculating the 4 texturecoordinates needed for our
  509. polygon drawer. In ordinary texturemapping we allways set these to the corners
  510. of the image. This is not the case in environmentmapping.
  511.  
  512. How DO we calculate the coordinated then ?? Well... it's allmost TOO easy.
  513. We simply use the POINT-normals again. As we have stored these in 8.8 fixed
  514. point we know all vectorcomponents ranges from -256 to 256.
  515. We just use the X and Y vectorcomponents for U and V TextureCoodinates needed
  516. for the point, so the only problem is to translate the value so it lies in the
  517. range 0 to 256.
  518. The solution is simple - divide the values by 2 and add 128 to the result.
  519. Voila! U and V coordinates for the Texturemapping routine is found..
  520.  
  521.  
  522. What about this phong shading then ?
  523. Well.. for a start I'll just briefly explain the working of REAL phong shading.
  524. As with all other types of shading the light-intensity is calculated by the
  525. dot-product. In Flatshading we did ONE dot-calculation to find the color.
  526. In gouraud we did FOUR dot-calculations to find the color at each of the 4
  527. points.
  528. In Phong shading you do a dot-calculation ON EVERY SINGLE PIXEL IN THE POLYGON!
  529. Ie. the light-intensity is calculated PRECISELY for every single pixel.
  530. So for each pixel we have to
  531.  
  532.            1) Calculate the Normal to the point by making a plane of the
  533.               neighbour pixels.
  534.            2) Make this normal a unitvector
  535.            3) Take the dot-product of the lightsource and the normal
  536.            4) Plot the pixel.
  537.  
  538. Well.. this don't sound like real time to me :)
  539. Numerous approximations to this routine has been made, but the easiest one is
  540. to simple do an environmentmap of a Phong-map.
  541. A phong map is simply a picture of a lightsource that we calculate. By using
  542. the environment mapping methode discussed above it is possible to do something
  543. that looks pretty much as phong shading.
  544. Check the sample program for the routine for calculating such a map. This is
  545. BTW not mine - took it somewhere but can't remember where.. Thank you whoever
  546. you are :)
  547.  
  548.  
  549.  
  550.  
  551. MULTIPLY LIGHTSOURCES
  552. -----------------------
  553.  
  554. Oh yeah.. before I forget. I think I promised to tell you how to implement
  555. multiple moving lightsources. And I'm not a man who breaks my word :)
  556.  
  557. First thing first : multiple lightsources. This is incredibly easy to implement.
  558. Instead of just having ONE lightvector you make an array with as many as you
  559. wish.
  560. When calculating the color you just add the lightintensities for all the
  561. lightsources together before multiplying with the number of shades. Of cause
  562. you has to make sure the intensity does not get bigger than 1.
  563.  
  564. As for moving lightsources - as the routines just use the lightvector in
  565. color-calculation you can freely move it around by assigning new values to it
  566. for each frame. Just remember to make it a unitvector.
  567.  
  568.  
  569.  
  570. OPTIMIZATIONS
  571. --------------
  572.  
  573. Now you got all the formulas you need to make nice fills/shadings.
  574. But it is up to you to optimize them. The goal of this tuturial is to make
  575. clear code that is easy to understand - in a tuturial I think that is more
  576. important than some highly optimized assembler code being thrown at you.
  577. Some people has mailed me telling me that my code was unoptimized... that I
  578. needed lots of stuff... like clipping, and more direct camera control.
  579. That is correct - but as mentioned before... this is not meant to be an
  580. example of my coding skills :)
  581. I have left many things out for the sake of easy understanding.
  582.  
  583. So now you know HOW and WHY the things work... it is then up to you to optimize
  584. them - to make that engine that is just a LITTLE bit faster than everyone
  585. elses..
  586. To see if you can beat Karl/Noone :)
  587.  
  588. I suggest the following optimizations :
  589.  
  590. 1) Clipping : easy in the normal polygon routine. In Gouraud and texturemap
  591.               you'll have to calculate new starting u,v / color values..
  592.  
  593. 2) Speed    : More assembler, draw two bytes at a time, use another polygon
  594.               drawer. Rotate the point-normals instead of calculating them.
  595.  
  596. 3) Triangles : Make triangle versions of ALL the drawing routines.. often
  597.                used in more complex 3d meshes.
  598.  
  599.  
  600. LAST REMARKS
  601. -------------
  602.  
  603. Well, that's about all for now.
  604. Hope you found this doc useful - and BTW : If you DO make anything public using
  605. these techniques please mention me in your greets or where ever you se fit.
  606. I DO love to see my name in a greeting :=)
  607.  
  608.  
  609. This completes to 3D tuturial serie. Hope I explained it well enough for you
  610. to get the grasp of it :)
  611. I myself is very pleased with these last two tuturials as I think they
  612. assembles all the most nessesary 3D theory in only two textfiles. Also they
  613. give the reader examples and explenations of things that I myself has never
  614. seen. Fx. gouraud and environmentmapping. I have never seen any good docs on
  615. these subjects - somehow people allways stopped writing tuturials when reaching
  616. these subjects.
  617.  
  618. But what now ??
  619. If you have any good ideas for a subject you wish to see a tuturial on please
  620. mail me. If I like the idea (and know anything about it :)  ) I'll write a
  621. tut on it.
  622. In near future I might write a small tuturial on how to use interrups for
  623. various programming problems. But, well.. after that I don't know.
  624.  
  625.  
  626.  
  627. Keep on coding...
  628.  
  629.   Telemachos - June '97.
  630.  
  631.  
  632.  
  633.  
  634.